﻿#include "Sdp.h"
#include <map>
#include <vector>
#include <string>
#include <cstring>
#include <QMap>
#include <QString>
#include <QDebug>
#include <QDateTime>
#include <QByteArray>
#include <QRegularExpression>
#ifdef _MSC_VER
#define sscanf sscanf_s
#endif // _MSC_VER
#define DEBUG_LOG(fmt)                                                                  \
	{                                                                                   \
		qDebug() << fmt << QDateTime::currentMSecsSinceEpoch() << __LINE__ << __func__; \
	}

/*--------------------Sdp start--------------------*/
int Sdp::parse(const QByteArray &buf)
{
	SdpTrack curTrack;
	bool isParsing = false;
	SdpTrack::Stream curStream = SdpTrack::Stream::NONE;

	for (const auto line : buf.split('\n'))
	{
		if (line.startsWith("m=")) // m=video 0 RTP/AVP 96 m=audio 0 RTP/AVP 97
		{
			if (curTrack.isAlive && curTrack.control_id != -1 && curTrack.control_id < TRACK_MAX_NUM)
				tracks[curTrack.control_id] = curTrack;

			char sdp_m_name[10] = {0};
			int sdp_m_seq = 0;
			char sdp_m_transport[10] = {0};
			int sdp_m_payload = 0;

			if (sscanf(line.data(), "m=%s %d %s %d\r\n", &sdp_m_name, &sdp_m_seq, &sdp_m_transport, &sdp_m_payload) != 4)
			{
				DEBUG_LOG("parse sdp m error");
			}

			if (!qstrcmp(sdp_m_name, "video"))
			{
				this->videoPayloadType = sdp_m_payload;
				curStream = SdpTrack::Stream::VIDEO;
			}
			else if (!qstrcmp(sdp_m_name, "audio"))
			{
				this->audioPayloadType = sdp_m_payload;
				curStream = SdpTrack::Stream::AUDIO;
			}
			else
			{
				DEBUG_LOG("parse sdp m name error");
				return -1;
			}

			curTrack = SdpTrack(true, sdp_m_payload);
			isParsing = true;
		}
		else if (isParsing)
		{
			if (line.startsWith("a=control:"))
			{
				char trackNameAndId[50] = {0};
				if (sscanf(line, "a=control:%s\r\n", &trackNameAndId) != 1)
				{
					DEBUG_LOG("parse sdp a=control error");
				}
				else
				{
					QByteArray tmp = QByteArray(trackNameAndId, strlen(trackNameAndId));

					// 去掉前面的url.赋值control
					curTrack.control = tmp.right(tmp.size() - tmp.lastIndexOf('/') - 1);
					QByteArrayList list = curTrack.control.split('=');
					if (list.size() == 2)
					{
						// 赋值control_name
						curTrack.control_name = list.at(0);
						// 赋值control_id
						curTrack.control_id = list.at(1).toInt();

						if (curStream == SdpTrack::Stream::VIDEO)
							this->videoStreamId = curTrack.control_id;
						else if (curStream == SdpTrack::Stream::AUDIO)
							this->audioStreamId = curTrack.control_id;
					}
					else
					{
						DEBUG_LOG("parse sdp a=control val error");
					}
				}
			}
			else if (line.startsWith("a=rtpmap:"))
			{
				char val[30] = {0};
				// if(sscanf(line, "%*[^ ] %s", &val) != 1)// %*[^ ]为空格前不取, *为不取, [^ ]到空格为止
				if (sscanf(line, "a=rtpmap:%*d %s\r\n", &val) != 1)
				{
					DEBUG_LOG("parse sdp a=rtpmap error");
				}
				else if (curStream == SdpTrack::Stream::VIDEO)
				{
					QByteArrayList list = QByteArray(val).split('/');
					if (list.size() == 2)
					{
						curTrack.codec = list.at(0);			// 视频编码格式
						curTrack.timebase = list.at(1).toInt(); // 视频时间基
					}
					else
					{
						DEBUG_LOG("parse sdp a=rtpmap:H264 val error");
					}
				}
				else if (curStream == SdpTrack::Stream::AUDIO)
				{
					QByteArrayList list = QByteArray(val).split('/');
					if (list.size() == 3)
					{
						curTrack.codec = list.at(0);				 // 音频编码格式
						curTrack.timebase = list.at(1).toInt();		 // 音频频率
						curTrack.audio_channel = list.at(2).toInt(); // 音频声道
					}
					else
					{
						DEBUG_LOG("parse sdp a=rtpmap:AAC val error");
					}
				}
				else
				{
					DEBUG_LOG(QString("unsupport stream id: %1").arg((int)curStream));
				}
			}
			else if (line.startsWith("b=AS"))
			{ // 建议带宽
				int as = 0;
				if (sscanf(line, "b=AS:%d\r\n", &as) != 1)
				{
					DEBUG_LOG("parse sdp b=AS error; text: " << as);
				}
				else
				{
					curTrack.as = as;
				}
			}
			else if (line.startsWith("a=fmtp:"))
			{ // a=fmtp:96 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=Z2QAKKzZQHgCJ+WagICAoAAAfSAAF3AR4wYywA==,aOk4TPI8;
				if (curStream == SdpTrack::Stream::VIDEO)
				{
					QRegularExpression spropRegex("sprop-parameter-sets=([^,]+),([^;]+)");
					QRegularExpressionMatch match = spropRegex.match(line);
					if (match.hasMatch())
					{
						this->spsBase64 = match.captured(1).toUtf8();
						this->ppsBase64 = match.captured(2).toUtf8();
					}
				}
			}
			else
			{
				// DEBUG_LOG("unsupport line: " << line)
			}
		}
		else
		{
			// DEBUG_LOG("unsupport line: " << line)
		}
	}

	if (curTrack.isAlive && curTrack.control_id < TRACK_MAX_NUM)
		tracks[curTrack.control_id] = curTrack;
	return 0;
}

SdpTrack *Sdp::popTrack()
{
	for (int index = 0; index < TRACK_MAX_NUM; index++)
	{
		if (tracks[index].isAlive && !tracks[index].is_send_setup)
		{
			tracks[index].is_send_setup = true;
			return &tracks[index];
		}
	}
	return nullptr;
}

void Sdp::clear()
{
	for (int index = 0; index < TRACK_MAX_NUM; index++)
	{
		tracks[index].isAlive = false;
		tracks[index].is_send_setup = false;
	}
}
bool Sdp::isEmpty() const
{
	for (int index = 0; index < TRACK_MAX_NUM; index++)
	{
		if (tracks[index].isAlive)
		{
			return false;
		}
	}
	return true;
}
/*--------------------Sdp end--------------------*/

QByteArray Sdp::getVideoCodeccparExtradata() const
{
	QByteArray spsBin = QByteArray::fromBase64(spsBase64);
	QByteArray ppsBin = QByteArray::fromBase64(ppsBase64);
	// 合并SPS和PPS，格式为：[start_code][SPS][start_code][PPS]
	QByteArray extradata;
	extradata.append("\x00\x00\x00\x01", 4); // SPS起始码
	extradata.append(spsBin);
	extradata.append("\x00\x00\x00\x01", 4); // PPS起始码
	extradata.append(ppsBin);
	return extradata;
}

void SdpTrack::print() const
{
	qDebug() << "isAlive      :" << isAlive << Qt::endl
			 << "control      :" << control << Qt::endl
			 << "control_name :" << control_name << Qt::endl
			 << "control_id   :" << control_id << Qt::endl
			 << "payload      :" << payload << Qt::endl
			 << "codec        :" << codec << Qt::endl
			 << "timebase     :" << timebase << Qt::endl
			 << "audio_channel:" << audio_channel << Qt::endl
			 << "audio_as	  :" << as << Qt::endl
			 << "is_send_setup:" << is_send_setup << Qt::endl;
}